Vim8 向けプラグインで Neovim 対応した話
https://gyazo.com/24357b935e7407d6d06e086a28f8b6f3
動機
私は Vim ユーザーなので初期は Vim8 のみ対応していたのですが、ほどほどに機能が安定してきた(?)ので Neovim ユーザーにも使ってもらえたら (デバッグするのに自分が) 嬉しいよね!という気持ちから Neovim 対応に踏み切りました。
/icons/注意.icon 注意
まだ正式サポートにしているわけではありません。
何となく動くことは確認していますが、不具合がある可能性はとても高いです。
求む Neovim で Clojure 開発していて、人柱しても良いという方!
やりたこと
開発している Vim プラグインは外部プロセスとして起動しているサーバーに接続した上で非同期にデータをやり取りすることでほぼ全ての機能を提供しています。
その際に Vim8 の channel を使っているので、channel 部分を Neovim でも動くようにできれば勝ったも同然なので、この点が Neovim 対応におけるゴールとしました。
ch_open と sockconnect
Vim8 と Neovim ではチャンネルの扱いがちょっと違います。
なので Vim script としては普通かもしれませんが、辞書の関数 を使ってインターフェイスは共通化させつつ、実装はVim/Neovimでそれぞれ異なる辞書を用意することで対応しました。 雑な例でいうと以下のような感じです。
code:vim_nvim_sample.vim
let s:vim = {}
let s:nvim = {}
function! s:vim.hello() abort
return 'hello vim'
endfunction
function! s:nvim.hello() abort
return 'hello neovim'
endfunction
let s:msg = has('nvim') ? s:nvim : s:vim
echo s:msg.hello()
具体的なコードは以下の通りで、基本的には Vim8 の channel 側をベースにして、Neovim の sockconnect を Vim8 と同様に使えるよう対応しました。
Vim 向けの実装
Neovim 向けの実装
環境毎の使い分け
躓いた点
Vim8 の ch_status に対応するものが Neovim にまだない (Neovim 0.3.1 時点)
ch_status はチャンネルが利用可能かどうかといったステータスを返してくれる関数です。 私が作っているプラグインは外部プロセスとして動いているサーバーに接続している関係上、サーバーが止まっていればそれを検知して良しなに処理する必要がありました。
しかし Neovim 0.3.1 の時点ではそれに相当する関数は用意されていなかったので、スマートではないですが、ひとまずデータを送信しようとして失敗したら接続が切れているとみなすようにしています。
もしよりスマートな方法があれば教えていただけるとありがたいです。
Vim8 の appendbufline に対応するものが nvim_buf_set_lines
厳密にはできることがちょっと異なり、nvim_buf_set_lines の方がより汎用的な作りになっていますが、同じ目的に使えるので採用しました。
こちらはちょっと雑ですが1つの関数内で処理を分岐させて対応などしています。(後々、ch_open/sockconnect と同様の対応に変更するかもしれません)
Neovim で trim 関数早く導入されないかなぁ
プルリクはマージされているので、きっと次のリリースには含まれるでしょう
少しだけ工夫した点
Vim と Neovim で挙動を変更するだけでなく、テスト時にも挙動を変更したい要望があったので、なんちゃって DI Container な仕組みを導入しています。
code:di_container.vim
function! iced#di#new_container() abort
let container = {}
function! container.register(name, builder) abort
if type(a:builder) != 2 | return | endif
endfunction
function! container.get(name) abort
let Builder = get(self, a:name, '')
if type(Builder) != 2 | return | endif
return Builder(self)
endfunction
return container
endfunction
Vim/Neovim の実装の違いは Builder 内で、テスト時には Builder 自体を差し替えるようにしています。
実際にテストで差し替えている個所は以下になります。
ただこの辺りはまだまだ試行錯誤している所なので、より良い方法があればどんどん採用していきたいと思っています。
最後に
これからも Vim と Neovim 間での差は出てくると思うので、Vim/Neovimの両対応を謳うプラグイン開発者としては辛いことも多いとは思いますが、Vim/Neovim 双方の発展を願って頑張っていきたい所存です。